/**
 * Created on 9 avr. 08 by Sebastian Audet
 */
package grid;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;

import tile.Tile;
import tile.TileSet;

/**
 * A grid is a <code>Collection</code> of Nodes, An orientation in which they are arranged,
 * usually either a triangle, square, or hex orientation, and is defined by its
 * finite size. Data can be loaded from external map files into the Nodes of the
 * Grid in tandem with the appropriate TileSet
 * @author Sebastian Audet
 * @param <GridSize>
 */
public interface Grid {
    
    int TRIANGLE = 329;
    int SQUARE = 330;
    int HEXAGON = 331;
    
    /**
     * @return Returns the type of grid that we have
     */
    int returnGridType();

    /**
     * A Class to handle Grid size
     * 
     * @author Sebastian Audet
     * 
     */
    class GridSize {
	int x = 0;
	int y = 0;

	GridSize(int width, int height) {
	    x = width;
	    y = height;
	}
    }

    // Node methods
    
    /**
     * Implements the grid orientation
     * @param node The Node who's neighbors we want to find
     * @return An array of Nodes Neighboring a particular node
     */
    Node[] getNeighborNodes(Node node);

    /**
     * Implements the grid orientation
     * @param node The Node who's neighbors we want to find
     * @return A <code>Collection</code> of Nodes Neighboring a particular node
     */
    Collection<Node> getNeighborNodesCollection(Node node);

    /**
     * @return An array of all the nodes in the Grid
     */
    Node[] getGridNodes();

    /**
     * @return A <code>Collection</code> of all the Nodes in the Grid
     */
    Collection<Node> getGridNodesCollection();

    /**
     * Technically, there can be no "empty" nodes, so this method is meant
     * to be implemented in terms of calling a specific TileSet level in the Nodes
     * and determining if that level is empty in the implementing code.
     * 
     * Since various implementations may exist, the safest is to assume that this 
     * method will return those nodes which are not activated
     * @return An array of "empty" Nodes
     */
    Node[] getEmptyNodes();
    
    /**
     * Returns all Nodes with a status of active
     * @return The array of Active Nodes
     */
    Node[] getActiveNodes();
    
    /**
     * Returns all Nodes with a status of active
     * @return The <code>Collection</code> of Active Nodes
     */
    Collection<Node> getActiveNodesCollection();
    
    /**
     * Returns all Nodes with a status of inactive
     * @return The array of In-Active Nodes
     */
    Node[] getInActiveNodes();
    
    /**
     * Returns all Nodes with a status of inactive
     * @return The Collection of In-Active Nodes
     */
    Collection<Node> getInActiveNodesCollection();

    /**
     * Technically, there can be no "empty" nodes, so this method is meant
     * to be implemented in terms of calling a specific TileSet level in the Nodes
     * and determining if that level is empty in the implementing code.
     * 
     * Since various implementations may exist, the safest is to assume that this 
     * method will return those nodes which are not activated
     * @return A <code>Collection</code> of "empty" Nodes 
     */
    Collection<Node> getEmptyNodesCollection();

    /**
     * Technically, there can be no "empty" nodes, so this method is meant
     * to be implemented in terms of calling a specific TileSet level in the Nodes
     * and determining if that level is empty in the implementing code.
     * 
     * Since various implementations may exist, the safest is to assume that this 
     * method will return those nodes which are not activated
     * @param node The node who's empty neighbors we want to get
     * @return An array of "empty" neighboring nodes
     */
    Node[] getEmptyNeighborNodes(Node node);

    /**
     * Technically, there can be no "empty" nodes, so this method is meant
     * to be implemented in terms of calling a specific TileSet level in the Nodes
     * and determining if that level is empty in the implementing code.
     * 
     * Since various implementations may exist, the safest is to assume that this 
     * method will return those nodes which are not activated
     * @param node The node who's empty neighbors we want to get
     * @return A <code>Collection</code> of "empty" neighbor Nodes
     */
    Collection<Node> getEmptyNeighborNodesCollection(Node node);
    
    /**
     * @param level The level(set) at which to search
     * @return The array of Nodes empty of a tile at the specified level
     */
    Node[] getEmptyNodes(int level);
    
    /**
     * @param level The level(set) at which to search
     * @return The <code>Collection</code> of Nodes empty of a tile at the specified level
     */
    Collection<Node> getEmptyNodesCollection(int level);
    
    /**
     * @param level The level at which to search
     * @param node The Node to search from
     * @return The array of Nodes lacking a <code>Tile</code> at that level
     */
    Node[] getEmptyNeighborNodes(int level, Node node);
    
    /**
     * @param level The level at which to search
     * @param node The Node from which to search
     * @return The Collection of Nodes lacking a <code>Tile</code> at that level
     */
    Collection<Node> getEmptyNeighborNodesCollection(int level, Node node);

    // Grid methods
    
    /**
     * @param tile the Tile to attempt to move
     * @param originatingNode The Node from which to remove the Tile
     * @param receivingNode The Node to which to add the Tile
     * @return Whether or not the tile could be moved
     */
    boolean moveTile(Tile tile,Node originatingNode,Node receivingNode);
    
    /**
     * Moves a Tile from one Node to another
     * @param tile The Tile to move
     * @param oloc The original location
     * @param rloc The new location
     * @return
     */
    boolean moveTile(Tile tile, Location oloc, Location rloc);
    
    /**
     * @param loc The location to get the node from
     * @return A Node
     * @throws GridOutOfBounds 
     */
    Node getNode(Location loc) throws GridOutOfBounds;

    /**
     * Sets the number of columns in the Grid.
     * Needs to perform a merge with the current grid.
     * @param rows The number of rows the grid should be
     * @param columns The number of columns the grid should be
     */
    void setGridSize(int rows, int columns);

    /**
     * Sets the number of columns in the Grid.
     * Needs to perform a merge with the current grid.
     * @param size the GridSize corresponding to the grid size of the grid
     */
    void setGridSize(GridSize size);

    /**
     * @return The number of rows in the grid (y)
     */
    int getRows();

    /**
     * @return The number of columns in the grid (x)
     */
    int getColumns();
    
    /**
     * Sets the number of columns in the Grid. (x)
     * Needs to perform a merge with the current grid.
     * @param columns The number of columns the Grid should be
     */
    void setColumns(int columns);
    
    /**
     * Sets the number of rows in the Grid. (y)
     * Needs to perform a merge with the current grid.
     * @param rows The number of rows the Grid should be
     */
    void setRows(int rows);

    /**
     * Reads in a mapping to the grid by reading in from a properly delimited File
     * a set of string values which it then adds to the array of Nodes by creating a Tile
     * with the supplied <code>TileSet</code> and String
     * @param file The delimited file from which to read
     * @param set The TileSet to use to create the Tiles
     * @return The boolean success of the operation
     * @throws GridOutOfBounds 
     */
    boolean readInGrid(File file, TileSet set) throws GridOutOfBounds;
    
    /**
     * Reads in a mapping to the grid by reading in from a properly delimited File
     * a set of string values which it then adds to the array of Nodes by creating a Tile
     * with the supplied <code>TileSet</code> and String
     * @param url
     * @param set
     * @return
     * @throws grid.GridOutOfBounds
     */
    boolean readInGrid(URL url, TileSet set) throws GridOutOfBounds;
    
    /**
     * Reads in a mapping to the grid by reading in from a properly delimited File
     * a set of string values which it then adds to the array of Nodes by creating a Tile
     * with the supplied <code>TileSet</code> and String
     * @param path
     * @param set
     * @return
     * @throws grid.GridOutOfBounds
     */
    boolean readInGrid(String path, TileSet set) throws GridOutOfBounds;

    /**
     * Reads in an activation grid by reading from a properly delimited File,
     * and activates each Node accordingly.
     * @param file The delimited file from which to read the activation map
     * @throws GridOutOfBounds 
     */
    void readInActivation(File file) throws GridOutOfBounds;
    
    /**
     * Reads in an activation grid by reading from a properly delimited File,
     * and activates each Node accordingly.
     * @param url The URL to search at
     * @throws grid.GridOutOfBounds
     */
    void readInActivation(URL url) throws GridOutOfBounds;
    
    /**
     * Reads in an activation grid by reading from a properly delimited File,
     * and activates each Node accordingly.
     * @param path
     * @throws grid.GridOutOfBounds
     */
    void readInActivation(String path) throws GridOutOfBounds;
    
    /**
     * Activates the grid based upon the data stored in the activation matrix.
     * @param activationmatrix
     * @throws GridOutOfBounds 
     */
    void activate(boolean[][] activationmatrix) throws GridOutOfBounds;
    
    /**
     * Activates the grid based upon the data stored in the activation matrix.
     * @param activationmatrix
     * @throws GridOutOfBounds 
     */
    void activate(ArrayList<ArrayList<Boolean>> activationmatrix) throws GridOutOfBounds;
    
    /**
     * Activates a Node at (x,y)
     * @param b The boolean of activation
     * @param x The x coordinate
     * @param y The y coordinate
     * @throws GridOutOfBounds 
     */
    void activate(boolean b,int x, int y) throws GridOutOfBounds;
    
    /**
     * Returns a 2D array of the Tiles present at any particular level
     * @param level The level at which to search
     * @return The 2D array of Tiles
     */
    Tile[][] getTileLevel(int level);
    
    /**
     * Returns a 2D Collection of the Tiles present at any particular level
     * @param level The level at which to search
     * @return The 2D Collection of Tiles
     */
    Collection<Collection<Tile>> getTileLevelCollection(int level);
    
    /**
     * Clears all Tiles of a certain level by accessing the nodes or their TileSets,
     * and checking whether or not the two match, if so, the tile is removed
     * @param level The TileSet to use to 
     * @return The boolean success of the operation
     */
    boolean removeTileLevel(int level);
    
    /**
     * Specifically removes the tile from the Node at location (x,y) 
     * @param tile The Tile to remove
     * @param x The x coordinate
     * @param y The y coordinate
     * @return The success of the operation
     */
    boolean removeTile(Tile tile,int x, int y);
    
    /**
     * Specifically adds the tile to the Node at location (x,y)
     * @param tile The Tile to add
     * @param x The x coordinate
     * @param y The y coordinate
     * @return The success of the operation
     */
    boolean addTile(Tile tile,int x, int y);
}
